﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Odbc;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Npgsql;

namespace VoteTranslator
{
    struct Wahlberechtigte
    {
        public int Id { get; set; }
        public int IdWahlkreis { get; set; }
        public int IdPerson { get; set; }
        public bool Gewaehlt { get; set; }
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            // Tabellen:
            // Bundeslaender: ID, Name, ID_Bundestagswahl
            // Bundestagswahl: ID, Jahr(int)
            // Direktkandidaten: ID, ID_Personen, ID_Wahlkreis
            // Landesliste: ID, ID_Person, ID_Bundesland, Position
            // Partei: ID, Name
            // Personen: ID, Vorname, Nachname, PLZ, Straße, ID_Partei, Jahrgang, ID_Bundestagswahl
            // Stimme: ID, ID_Erststimme, ID_Zweitstimme, ID_Wahlkreis
            // Wahlberechtigte: Gewählt(bool), ID_Wahlkreis, ID_Person
            // Wahlkreis: ID, Nummer(int), Name, AnzahlWahlberechtigte, AnzahlAbgegebeneStimmen, ID_Bundesland

            /* Schon erstellte Tabellen:
             * Direktkandidaten
             * Bundestagswahl
             * Landesliste
             * Partei
             * Personen
             * Wahlberechtigte  
             * Wahlkreis
             * Bundeslaender
             * -> Nur noch die Stimmen sind interessant
             * für jede Stimme eigenen Eintrag in der CSV-Datei erstellen, ebenso für Wahlberechtigte
             
            */

            NpgsqlConnection conn;

            try
            {
                // PostgreSQL-style connection string
                string connstring = String.Format("Server={0};Port={1};User Id={2};Password={3};Database={4};",
                                                  "127.0.0.1", "5432", "postgres", "dbprojekt", "Bundestagswahlen");
                // 137.250.170.84
                conn = new NpgsqlConnection(connstring);
                conn.Open();
            }
            catch (Exception msg)
            {
                Console.WriteLine(msg.ToString());
                throw;
            }

            const string bundestagswahljahr = "2005"; // entspricht der Wahl 2009
            
            // Dictionary, um von Parteinamen auf interne ID zu kommen
            IDictionary<String, int> partyDict = new Dictionary<string, int>();

            using (NpgsqlCommand command = new NpgsqlCommand("select * from \"Parteien\"", conn))
            {
                NpgsqlDataReader dr = command.ExecuteReader();
                while (dr.Read())
                {
                    partyDict.Add((string) dr[1], (int) dr[0]);
                }
            }
            // partyDict.Add("Übrige", 0); //Unabhängige Direktkandidaten haben Partei mit der Nummer 0

            // Übersetzung von Wahlkreisnummer (1-299) und Wahljahr zu Wahlkreis-ID (im System)
            // Liste der Bundesland_IDs der zu untersuchenden Wahl
            IDictionary<string, string> wahlkreisNRzuID = new Dictionary<string, string>();

            using (NpgsqlCommand command = new NpgsqlCommand("select \"Wahlkreise\".Nummer, \"Wahlkreise\".IDwahlkreis from \"Bundestagswahlen\", \"Bundeslaender\", \"Wahlkreise\" " +
                                                             "where \"Bundestagswahlen\".Jahr = '" + bundestagswahljahr + "' and " +
                                                             "\"Bundestagswahlen\".IDBundestagswahl = \"Bundeslaender\".IDBundestagswahl and " +
                                                             "\"Bundeslaender\".idbundesland = \"Wahlkreise\".IDBundesland", conn))
            {
                NpgsqlDataReader dr = command.ExecuteReader();
                while (dr.Read())
                {
                    wahlkreisNRzuID.Add(dr[0].ToString(), dr[1].ToString());
                }
            }

            // Übersetzung von Wahlkreis_ID und Partei nach Direktkandidat

            // Zuerst ein Dictionary, das Wahlkreis_ID abbildet zu einer Liste von Tupeln (DirektkandidatenNr, PersonNr)
            IDictionary<String, IList<String[]>> wahlkreisNachDirekt = new Dictionary<string, IList<string[]>>();

            using (NpgsqlCommand command = new NpgsqlCommand("select iddirektkandidat, idperson, idwahlkreis from \"Direktkandidaten\" where idwahlkreis in " + 
                                                             "(select \"Wahlkreise\".IDwahlkreis from \"Bundestagswahlen\", \"Bundeslaender\", \"Wahlkreise\" " +
                                                             "where \"Bundestagswahlen\".Jahr = '" + bundestagswahljahr + "' and " +
                                                             "\"Bundestagswahlen\".IDBundestagswahl = \"Bundeslaender\".IDBundestagswahl and " +
                                                             "\"Bundeslaender\".IDBundesland = \"Wahlkreise\".IDBundesland)", conn))
            {
                NpgsqlDataReader dr = command.ExecuteReader();
                while (dr.Read())
                {
                    string idDirektkandidat = dr[0].ToString();
                    string idPerson = dr[1].ToString();
                    string idWahlkreis = dr[2].ToString();
                    if (!wahlkreisNachDirekt.ContainsKey(idWahlkreis))
                        wahlkreisNachDirekt.Add(idWahlkreis, new List<string[]>());
                    wahlkreisNachDirekt[idWahlkreis].Add(new string[] {idDirektkandidat, idPerson});
                }
            }
            
            // Dictionary, dass PersonNr auf ParteiNr abbildet
            IDictionary<String, String> personNachPartei = new Dictionary<string, string>();

            using (NpgsqlCommand command = new NpgsqlCommand("select IDPerson, IDPartei from \"Personen\" where idbundestagswahl =" +
                                                            "(select idbundestagswahl from \"Bundestagswahlen\" where Jahr = '" + bundestagswahljahr + "')", conn))
            {
                NpgsqlDataReader dr = command.ExecuteReader();
                while (dr.Read())
                {
                    if (!(dr[1].ToString()).Equals("")) // Falls die Person einer Partei angehört, füge sie dem Dictionary hinzu
                    {
                        personNachPartei.Add(dr[0].ToString(), dr[1].ToString());
                    }
                }
            }

            // Für jeden Wahlkreis ein Dictionary, das ParteiNr nach Direktkandidaten-Nr abbildet.
            IDictionary<String, IDictionary<String, String>> direktDict = new Dictionary<string, IDictionary<string, string>>();

            foreach (KeyValuePair<string,IList<string[]>> kvpair in wahlkreisNachDirekt)
            {
                IList<string[]> direktkandiListe = kvpair.Value;
                
                direktDict.Add(kvpair.Key, new Dictionary<string, string>());
                foreach (string[] direktkandi in direktkandiListe)
                {
                    if (personNachPartei.ContainsKey(direktkandi[1])) // Direktkandidat ist Mitglied einer Partei
                    {
                        direktDict[kvpair.Key].Add(personNachPartei[direktkandi[1]], direktkandi[0]);
                    } 
                    else // Direktkandidat ist parteilos
                    {
                        // füge nur einen Direktkandidaten hinzu, der alle für parteilose Kandidaten abgegebenen Erststimmen
                        // erhalten wird der implementierungshalber der Partei Übrige angehört
                        // Genauere Informationen zu den parteilosen Direktkandidaten sind in den Ergebnissen nicht enthalten.
                        if (!direktDict[kvpair.Key].ContainsKey(partyDict["Übrige"].ToString()))
                          direktDict[kvpair.Key].Add(partyDict["Übrige"].ToString(), direktkandi[0]);
                    }
                }
            }

            // Liste der Parteien in der Reihenfolge, wie sie in der Ergebnistabelle vorkommen
            IList<string> partyList = new List<string>();

            // Verwende deutsches ANSI-Encoding für die kerg.csv-Datei (System.Text.Encoding.Default)
            const string svnpath = @"C:\Users\dbprojekt\Documents\datenbanken-se\datenbanken-se\";
            using (StreamReader sr = new StreamReader(svnpath + @"Import Daten\kerg2005.csv", System.Text.Encoding.Default))
            // using (StreamWriter wahlbewr = new StreamWriter(svnpath + @"Export Daten\gen_wahlberechtigte.csv"))
            using (StreamWriter erstStimmenDatei = new StreamWriter(svnpath + @"Export Daten\gen_erststimmen.csv"))
            using (StreamWriter zweitStimmenDatei = new StreamWriter(svnpath + @"Export Daten\gen_zweitstimmen.csv"))
            {
                string line = null;
                while ((line = sr.ReadLine()) != null)
                {
                    string[] values = line.Split(';');

                    // Leerzeilen überspringen
                    if (values.Length <= 1 || values[0].Equals(""))
                        continue;

                    // Ersten zwei Zeilen in der Tabelle überspringen
                    if (values[0].StartsWith("Bundestagswahl 20") || values[0].Equals("Endgültig"))
                        continue;

                    // Dritte Zeile: Parteien auslesen
                    if (values[0].Equals("Nr"))
                    {
                        // Mögliche Parteien erfassen. Wichtig: müssen sortiert sein! (-> List)
                        // 19 entspricht der Position der ersten Partei in der Tabelle
                        // kerg.csv (Ergebnisse 2009 vom Bundeswahlleiter)
                        for (int i = 19; i < values.Length - 1; i = i + 4)
                        {
                            partyList.Add(values[i]);
                        }
                        continue;
                    }

                    // Vierte und fünfte Zeile überspringen
                    if (values[2].Equals("zu") || values[3].Equals("Endgültig"))
                        continue;
                   
                    // Zeilen der einzelnen Länder (gehört zu = 99) und des Bundesgebietes (Nr. 99) überspringen
                    if (values[1].Equals("\"Bundesgebiet\"") || values[1].Equals("Bundesgebiet") || values[2].Equals("99"))
                        continue;


                    /* Wird aktuell noch nicht benötigt, evtl. interessant für Wahlmaschinen Backend, wenn wir überprüfen müssen, ob jemand gewählt hat
                     * 
                     *                     
                    // Alle restlichen Zeilen sind Zeilen eines einzelnen Wahlkreises
                    int anzahlWahlBe = Convert.ToInt32(values[3]);
                    int anzahlWaehler = Convert.ToInt32(values[7]);
                    int nochEinzufWaehler = anzahlWaehler;
                    for (int nochEinzufWahlBe = anzahlWahlBe; nochEinzufWahlBe > 0; nochEinzufWahlBe--)
                    {
                        bool gewaehlt = false;
                        if (nochEinzufWaehler > 0)
                        {
                            gewaehlt = true;
                            nochEinzufWaehler--;
                        }

                        // (ID, gewählt, ID_Person, ID_Wahlkreis)
                        // WahlkreisNr entspricht values[2]
                        // wahlbewr.WriteLine((anzahlWahlBe - nochEinzufWahlBe) + (gewaehlt ? ",1," : ",0,") + "0," + wahlkreisNRzuID[values[0]]);
                        // TODO: Ein Personenobjekt pro Wahlberechtigen erstellen
                    }
                    */
                    Console.WriteLine("Wahlkreis:" + "   " + values[0]);
                    Console.WriteLine("Spezialfall ungültige Stimmen");
                    int erstStimmen = (values[1] == "" ? 0 : Convert.ToInt32(values[11]));
                    int zweitStimmen = (values[13] == "" ? 0 : Convert.ToInt32(values[13]));
                    Console.WriteLine("Ungültige; Erststimmen: " + erstStimmen + "; Zweitstimmen: " + zweitStimmen);
                    // wenn für die aktuelle Erststimmenpartei keine Stimmen mehr ausstehen, gehe zur nächsten Partei über
                    while (erstStimmen != 0)
                    {
                        // (ID, ID_Erststimme, ID_Zweitstimme, ID_Wahlkreis)
                        // WahlkreisNr entspricht values[2]
                        erstStimmenDatei.WriteLine("," + wahlkreisNRzuID[values[0]]);
                        erstStimmen--;
                    }
                    while (zweitStimmen != 0)
                    {
                        // (ID, ID_Erststimme, ID_Zweitstimme, ID_Wahlkreis)
                        // WahlkreisNr entspricht values[2]
                        zweitStimmenDatei.WriteLine("," + wahlkreisNRzuID[values[0]]);
                        zweitStimmen--;
                    }

                    for (int aktuellePartei = 0; aktuellePartei < partyList.Count; aktuellePartei++)
                    {
                        
                        erstStimmen = (values[19 + aktuellePartei * 4] == "" ? 0: Convert.ToInt32(values[19 + aktuellePartei * 4]));
                        zweitStimmen = (values[21 + aktuellePartei * 4] == "" ? 0 : Convert.ToInt32(values[21 + aktuellePartei * 4]));
                        Console.WriteLine("Partei: " + partyList[aktuellePartei] + "; Erststimmen: " + erstStimmen + "; Zweitstimmen: " + zweitStimmen);
                        // wenn für die aktuelle Erststimmenpartei keine Stimmen mehr ausstehen, gehe zur nächsten Partei über
                        while (erstStimmen != 0)
                        {
                            // (ID, ID_Erststimme, ID_Zweitstimme, ID_Wahlkreis)
                            // WahlkreisNr entspricht values[2]
                            erstStimmenDatei.WriteLine(direktDict[wahlkreisNRzuID[values[0]]][partyDict[partyList[aktuellePartei]].ToString()] + "," + wahlkreisNRzuID[values[0]]);
                            erstStimmen--;
                        }
                        while (zweitStimmen != 0)
                        {
                            // (ID, ID_Erststimme, ID_Zweitstimme, ID_Wahlkreis)
                            // WahlkreisNr entspricht values[2]
                            zweitStimmenDatei.WriteLine(partyDict[partyList[aktuellePartei]] + "," + wahlkreisNRzuID[values[0]]);
                            zweitStimmen--;
                        }


                        /* erststimmenpartei = Position, in der die Partei in der Ergebnistabelle vorkommt.
                         * partyList[erststimmenpartei]: Name der Partei an einer bestimmten Position
                         * partyDict[partyList[erststimmenpartei]]: Interne Nr (ID) der Partei mit diesem Namen im DBMS
                         * direktDict[wahlkreisNRzuID[values[2]]]: Dictionary, welches Parteien-IDs auf Direktkandidaten-IDs auszählt, pro Wahlkreis gibt es eines
                         * direktDict[wahlkreisNRzuID[values[2]]][partyDict[partyList[erststimmenpartei]].ToString()]:
                         * Direktkandidaten-ID des Kandidaten der Partei, welche in der Ergebnistabelle an der aktuellen Position aufgezählt ist,
                         * bzw. der erste parteilose Direktkandidat in diesem Wahlkreis, falls wir gerade die Partei "Übrige" betrachten.
                         * */
                    }
                }
            }

            Console.WriteLine("Ende");
            Console.ReadLine();
        }
        
    }
}
